function _MoveToIndex(arr, value, oldIndex, newIndex) {
    if(oldIndex >= 0) {
        newIndex = Math.max(0, newIndex);
        if(oldIndex === newIndex) return oldIndex;
        arr.splice(oldIndex, 1);
        arr.splice(newIndex, 0, value);
        return arr.indexOf(value);
    }
    return -1;
}

function _Insert(key, value, _map, _arr, index) {
    const _idx = _arr.indexOf(value);
    if(_idx >= 0) return false;
    index = Math.max(0, index);
    _arr.splice(index, 0, value);
    _map.set(key, value);
    return true;
}

function _Remove(key, value, _map, _arr) {
    const _i = _arr.indexOf(value);
    if(_i >= 0) {
        _arr.splice(_i, 1);
        _map.delete(key);
        return true;
    }
    return false;
}

export default function Collection(keyName) {
    const _KEY_ = keyName;
    const _arr = [];
    const _map = new Map();

    return {
        /**
         * index<=0 -> index of 0;
         * index>=array.length -> index of array.length;
         */
        Insert: (value, index = Number.MAX_SAFE_INTEGER) => {
            return _Insert(value[_KEY_], value, _map, _arr, index);
        },

        InsertBefore(value, E) {
            const _idx = _arr.indexOf(E);
            if(_idx >= 0) {
                return _Insert(value[_KEY_], value, _map, _arr, _idx);
            }
            return false;
        },

        InsertAfter(value, E) {
            const _idx = _arr.indexOf(E);
            if(_idx >= 0) {
                return _Insert(value[_KEY_], value, _map, _arr, _idx + 1);
            }
            return false;
        },

        Remove: (value) => {
            return _Remove(value[_KEY_], value, _map, _arr);
        },

        RemoveByKey: (key) => {
            return _Remove(key, _map.get(key), _map, _arr);
        },

        RemoveByIndex: (index) => {
            const _value = _arr[index];
            return _Remove(_value[_KEY_], _value, _map, _arr);
        },

        Clear: () => {
            _arr.length = 0;
            _map.clear();
        },

        GetByIndex: (index) => {
            return _arr[index];
        },

        GetByKey: (key) => {
            return _map.get(key);
        },

        IndexOf: (value) => {
            return _arr.indexOf(value);
        },

        KeyOf: (value) => {
            return value[_KEY_];
        },

        MoveToIndex: (value, index) => {
            const _index = _arr.indexOf(value);
            return _MoveToIndex(_arr, value, _index, index);
        },

        MoveUp: (value) => {
            const _index = _arr.indexOf(value);
            return _MoveToIndex(_arr, value, _index, _index - 1);
        },

        MoveDown: (value) => {
            const _index = _arr.indexOf(value);
            return _MoveToIndex(_arr, value, _index, _index + 1);
        },

        Has: (value) => {
            return _map.has(value[_KEY_]);
        },

        HasKey: (key) => {
            return _map.has(key);
        },

        ForEach: (fn, thisArg) => {
            for(let i = 0, N = _arr.length; i < N; ++i) {
                if(fn.call(thisArg, _arr[i], i)) break;
            }
        },

        ForEachInverse: (fn, thisArg) => {
            for(let i = _arr.length - 1; i >= 0; --i) {
                if(fn.call(thisArg, _arr[i], i)) break;
            }
        },

        get length() {return _arr.length;},

        DebugPrint: (msg) => {
            console.group(msg);
            for(let i = 0, N = _arr.length; i < N; ++i) {
                console.log('key:', _arr[i]);
            }
            console.groupEnd();
        }
    }
}
